//变量和常量

#include <iostream>
#include <assert.h>
#include <bitset>
#include <utility>
#include <array>
#include <vector>
#include <map>

using namespace std;

class Const final
{
private:
    int num = 0;
    mutable int counter = 0; //mutable只能修饰类里面的成员变量表示变量即使是const对象里也是可以修改的

public:
    void func() const // 不可变函数 不会修改对象的状态（成员变量），函数执行的只读操作
    {
        // num++;
    }

    void func2()
    {
        num++;
    }

    void func1(int num)
    {
        num++;
    }

    void func3() const  //const 成员函数
    {
        cout << "func3 const called!" << endl;
        // 即使是const函数也可以修改mutable修饰的变量，相当于在const对象上打了一个补丁，
        // 让某些特殊的变量允许修改。因为即使是一个const的对象也有可能有一些成员变量是需要能够被修改的。
        // 它只能修饰类里面的成员变量
        counter++;
    }

    void func3() //const和不加const的相同成员函数可以互为重载关系 如果调用这个方法的引用是const的则调用const版本 否则就是非const版本
    {
        cout << "func3 non const called!" << endl;
        counter++; 
    }
};

// 真正的编译期常量 只能修饰基本类型和字符串字面量 array因为能够在编译器确定长度是可以使用constexpr的 不能修饰map string等需要动态变化的容器类型
constexpr int MAX = 100;
constexpr auto ss = "ss";
array<int, MAX> arr1 = {0};
constexpr array<int, 3> arr {1,2,3};
// constexpr vector<int> vec; 编译错误
// constexpr map<int, int> m; 编译错误

//编译器常量函数 修饰函数的时候要求不能有运行期的特性 比如trycatch 不能有动态分配内存等。。。
constexpr long mega_bits()
{
    return 1024 * 1024;
}

bitset<mega_bits()> bits; //编译器常量可用于模板参数

void const_func()
{
    /*
    如果只是const 那么在编译期会把const变量优化 替换成相应的原始值，从而将运行阶段的常量转换为编译阶段的常量。所以虽然我们用指针修改了常量的值
    但这个值在运行阶段根本没有用到，它被编译器优化掉了。如果加上volatile，就会告诉编译器这个变量有以难以察觉的方式（操作系统信号等）被改变。提示
    编译器其不稳定会变化，告知编译器别自作主张的去优化。在读取这个变量的代码处会生成去内存取值的CPU指令，而不是自作主张的直接替换成常量值了。
    */
    const volatile int MAX_LEN = 1024;
    auto ptr = const_cast<int *>(&MAX_LEN); //const_cast c++4个转型操作符之一 专门用来去除常量性 最好不用
    *ptr = 2048;
    std::cout << MAX_LEN << std::endl;

    // const指针
    string name = "zhoubing";
    const string *p_name = &name;           // name是常量
    string *const k_p_name = &name;         // 指针是常量
    const string *const k_p_k_name = &name; // name和指针都是常量

    decltype(auto) n = as_const(name);
    assert(is_reference_v<decltype(n)>);    //断言是引用类型
    assert(is_const_v<remove_reference_t<decltype(n)>>); //断言常量性 去掉类型里的引用

    // Const cst;
    // auto& a = as_const(cst); //报错 const类型的引用只能调用const函数
    // a.func();

    Const cst;
    cst.func3();
    auto &a1 = as_const(cst);
    a1.func3(); //通过 const类型的引用或者指针只能调用const函数
}

int main()
{
    const_func();
}